// regex_parse.cpp
// compile with: /clr
#using <system.dll>

#include <stdio.h>
#include <stdlib.h>
#include "global.h"

using namespace System;
using namespace System::Text::RegularExpressions;
using namespace System::Collections;

#define MAXLINE 100

int findNode(String^ name){

	int index = -1;
	for each (Node^ np in Global::nodeAll){
		if(np->name->Equals(name)){
			index = np->index;
			break;
		}
	}

	return index;
}

void printList(IEnumerable^ myList){
	IEnumerator^ myEnum = myList->GetEnumerator();
	while(myEnum->MoveNext()){
		Node^ node = safe_cast<Node^>(myEnum->Current);
		Console::Write(" {0}", node->name);
	}
	Console::Write("\t\t");
}

void printNode(){
	
	Console::WriteLine("index\ttype\tname\tgate\tfanin\tfanout\tlevel");
	for each (Node^ np in Global::nodeAll){
		Console::Write("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", np->index, np->type,
			np->name, np->gate, np->fanin, np->fanout, np->level);
//		printList(np->upperNodes);
//		printList(np->downNodes);
		Console::WriteLine();
	}

	Console::Write("primary input: ");
	for each (Node^ np in Global::nodeInput){
		Console::Write("{0} ", np->name);
	}
	Console::WriteLine();

	Console::Write("primary output: ");
	for each (Node^ np in Global::nodeOutput){
		Console::Write("{0} ", np->name);
	}
	Console::WriteLine();
}

int nodeReady(Node^ np){
	int flag = 1;
	IEnumerator^ myEnum = np->upperNodes->GetEnumerator();
	while(myEnum->MoveNext()){
		Node^ node = safe_cast<Node^>(myEnum->Current);
		if(node->level == -1){
			flag = 0;
			break;
		}
	}
	return flag;
}

int nodeCompute(Node^ np){
	int level = -1;
	IEnumerator^ myEnum = np->upperNodes->GetEnumerator();
	while(myEnum->MoveNext()){
		Node^ node = safe_cast<Node^>(myEnum->Current);
		if(node->level > level){
			level = node->level;
		}
	}
	return level + 1;
}

int nodeLevelize(){

	Queue^ nodeQ = gcnew Queue();
	IEnumerator^ myEnum;
	Node^ node, ^ppi;
	IDictionaryEnumerator^ denum;
	DictionaryEntry dentry;

	//initialize the queue
	for each (Node^ np in Global::nodeInput){
		np->level = 0;
		myEnum = np->downNodes->GetEnumerator();
		while(myEnum->MoveNext()){
			node = safe_cast<Node^>(myEnum->Current);
			if(nodeReady(node)){
				nodeQ->Enqueue(node);
			}
		}
	}

	denum = Global::nodeFF->GetEnumerator();
	while(denum->MoveNext()){
		dentry = (DictionaryEntry)denum->Current;
		ppi = (Node^)dentry.Value;
		ppi->level = 0;
		myEnum = ppi->downNodes->GetEnumerator();
		while(myEnum->MoveNext()){
			node = safe_cast<Node^>(myEnum->Current);
			if(nodeReady(node)){
				nodeQ->Enqueue(node);
			}
		}
	}

	while(nodeQ->Count != 0){
		node = (Node^)nodeQ->Dequeue();
		node->level = nodeCompute(node);
		myEnum = node->downNodes->GetEnumerator();
		while(myEnum->MoveNext()){
			node = safe_cast<Node^>(myEnum->Current);
			if(nodeReady(node)){
				nodeQ->Enqueue(node);
			}
		}
	}

	return 0;
}


int main( )
{
	FILE* fileHandle;
	char buffer[MAXLINE];
	char keyword[MAXLINE];
	String^ keyWire = "wire";
	String^ keyInput = "input";
	String^ keyOutput = "output";
	String^ line;
	String^ key;

	int inputCount = 0;
	int wireCount = 0;
	int outputCount = 0;
	int allCount = 0;
	int busCount = 0;
	
	String^ pattern;
	Regex^ regex;

	fpos_t fpos;
	Node^ nodePtr;
	array<wchar_t>^ trimChar = {',', ';', '(', ')'};
	array<wchar_t>^ splitChar = {' '};

	Node^ nodeA, ^nodeB, ^nodeC, ^nodeD;
	Node^ nodeY;
	Node^ nodeCLK, ^nodeQ;

	int inputNum = 0;
	
	//open verilog file
	if((fileHandle = fopen("fsm.syn.v", "r")) == NULL){
		perror("fopen has error");
		exit(1);
	}
	fgetpos(fileHandle, &fpos);

	//read verilog file for the first time
	while(fgets(buffer, MAXLINE, fileHandle) != NULL){
		line = gcnew String(buffer);
		if(sscanf(buffer, "%s ", keyword) == 1){
			key = gcnew String(keyword);
			if(key->Equals(keyWire)){
				pattern = "[a-zA-Z0-9_]+[,;]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success;
					match = match->NextMatch()){
						wireCount++;
						Console::WriteLine("{0}", match->Value);
				}
				Console::WriteLine("number of wire: {0}", wireCount);
			}else if(key->Equals(keyOutput)){
				pattern = "\\[[0-9]:[0-9]\\]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success;
					match = match->NextMatch()){
						Console::WriteLine("{0}", match->Value);
						outputCount = Int32::Parse((match->Value)->Substring(1,1)) + 1;
				}
				Console::WriteLine("number of output: {0}", outputCount);
			}else if(key->Equals(keyInput)){
				pattern = "[a-zA-Z0-9_]+[,;]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success;
					match = match->NextMatch()){
						inputCount++;
						Console::WriteLine("{0}", match->Value);
				}
				Console::WriteLine("number of input: {0}", inputCount);
			}
		}
	}

	Global::nodeAll = gcnew array<Node^>(inputCount+wireCount+outputCount);
	Global::nodeInput = gcnew array<Node^>(inputCount);
	Global::nodeOutput = gcnew array<Node^>(outputCount);
	Global::nodeFF = gcnew Hashtable();
	inputCount = 0;
	outputCount = 0;
	allCount = 0;

	fsetpos(fileHandle, &fpos);
	//read the verilog for the second time
	while(fgets(buffer, MAXLINE, fileHandle) != NULL){
		line = gcnew String(buffer);
		if(sscanf(buffer, "%s ", keyword) == 1){
			key = gcnew String(keyword);

			if(key->Equals(keyWire)){
				pattern = "[a-zA-Z0-9_]+[,;]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodePtr = gcnew Node(allCount, match->Value->TrimEnd(trimChar), NType::GATE);
					Global::nodeAll->SetValue(nodePtr, allCount++);
				}
			}else if(key->Equals(keyOutput)){
				pattern = "\\[[0-9]:[0-9]\\]\\s[a-zA-Z0-9_]+[,;]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					busCount = Int32::Parse((match->Value)->Substring(1,1)) + 1;
					array<String^>^ splitString = match->Value->Split(splitChar);
					for(int i = 0; i < busCount; i++){
						String^ newName = String::Concat(((String^)(splitString->GetValue(1)))->TrimEnd(trimChar), "[", i, "]");
						nodePtr = gcnew Node(allCount, newName, NType::PO);
						Global::nodeAll->SetValue(nodePtr, allCount++);
						Global::nodeOutput->SetValue(nodePtr, outputCount++);
					}
				}
			}else if(key->Equals(keyInput)){
				pattern = "[a-zA-Z0-9_]+[,;]";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodePtr = gcnew Node(allCount, match->Value->TrimEnd(trimChar), NType::PI);
					nodePtr->gate = GType::IPT;
					Global::nodeAll->SetValue(nodePtr, allCount++);
					Global::nodeInput->SetValue(nodePtr, inputCount++);
				}
			}
		}
	}

	fsetpos(fileHandle, &fpos);
	//read the verilog for the third time
	while(fgets(buffer, MAXLINE, fileHandle) != NULL){
		line = gcnew String(buffer);
		if(sscanf(buffer, "%s ", keyword) == 1){
			key = gcnew String(keyword);

			if(key->Equals("INVX1") || key->Equals("INVX2")){
				pattern = "\\.A\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeA = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
					break;
				}
				pattern = "\\.Y\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeY = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
					break;
				}
				nodeA->fanout = 1;
				nodeA->downNodes->Add(nodeY);
				nodeY->fanin = 1;
				nodeY->upperNodes->Add(nodeA);
				nodeY->gate = GType::INVX1;
			}else if(key->Equals("DFFPOSX1") || key->Equals("DFFNEGX1")){
				pattern = "\\.D\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeD = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
					break;
				}
				pattern = "\\.CLK\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeCLK = Global::nodeAll[findNode((match->Value->Remove(0, 5))->TrimEnd(trimChar))];
					break;
				}
				pattern = "\\.Q\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeQ = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
					break;
				}
				nodeD->fanout = 0;
				nodeD->type = NType::PPO;
				nodeQ->fanin = 0;
				nodeQ->type = NType::PPI;
				Global::nodeFF->Add(nodeD, nodeQ);
			}else if(Enum::IsDefined(GType::typeid, key)){
				pattern = "[0-9]+X";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(key); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					if(match->Value->Length == 2){
						inputNum = Int32::Parse(match->Value->Substring(0, 1));
					}else if(match->Value->Length == 3){
						inputNum = Int32::Parse(match->Value->Substring(0, 1)) + Int32::Parse(match->Value->Substring(1, 1));
					}else{
						Console::WriteLine("keyword has error");
					} 
					break;
				}

				for(int i = inputNum; i > 0; i--){
					switch (i){
					case 1:
						{
							pattern = "\\.A\\([a-zA-Z0-9_\\[\\]]+\\)";
							regex = gcnew Regex(pattern);
							for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
								Console::WriteLine("{0}", match->Value);
								nodeA = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
								break;
							}
							break;
						}
					case 2:
						{
							pattern = "\\.B\\([a-zA-Z0-9_\\[\\]]+\\)";
							regex = gcnew Regex(pattern);
							for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
								Console::WriteLine("{0}", match->Value);
								nodeB = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
								break;
							}
							break;
						}
					case 3:
						{
							pattern = "\\.C\\([a-zA-Z0-9_\\[\\]]+\\)";
							regex = gcnew Regex(pattern);
							for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
								Console::WriteLine("{0}", match->Value);
								nodeC = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
								break;
							}
							break;
						}
					case 4:
						{
							pattern = "\\.D\\([a-zA-Z0-9_\\[\\]]+\\)";
							regex = gcnew Regex(pattern);
							for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
								Console::WriteLine("{0}", match->Value);
								nodeD = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
								break;
							}
							break;
						}
					}
				}
				pattern = "\\.Y\\([a-zA-Z0-9_\\[\\]]+\\)";
				regex = gcnew Regex(pattern);
				for(Match^ match = regex->Match(line); match->Success; match = match->NextMatch()){
					Console::WriteLine("{0}", match->Value);
					nodeY = Global::nodeAll[findNode((match->Value->Remove(0, 3))->TrimEnd(trimChar))];
					break;
				}	
				for(int i = inputNum; i > 0; i--){
					switch (i){
					case 1:
						{
							nodeA->fanout = nodeA->fanout++;
							nodeA->downNodes->Add(nodeY);
							nodeY->upperNodes->Add(nodeA);
							break;
						}
					case 2:
						{
							nodeB->fanout = nodeB->fanout++;
							nodeB->downNodes->Add(nodeY);
							nodeY->upperNodes->Add(nodeB);
							break;
						}
					case 3:
						{
							nodeC->fanout = nodeC->fanout++;
							nodeC->downNodes->Add(nodeY);
							nodeY->upperNodes->Add(nodeC);
							break;
						}
					case 4:
						{
							nodeD->fanout = nodeD->fanout++;
							nodeD->downNodes->Add(nodeY);
							nodeY->upperNodes->Add(nodeD);
							break;
						}
					}
				}
				nodeY->fanin = inputNum;
				nodeY->gate = (GType)Enum::Parse(GType::typeid, key);
			}
		}
	}			 

	fclose(fileHandle);

	nodeLevelize();
	printNode();
	return 0;
}